// $Id: exception.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#include <xpcom/config.hpp>
#include <new> // std::bad_alloc
#ifdef XPCOM_USE_STD_EXCEPTIONS
#include <stdexcept>
#ifndef BOOST_NO_RTTI
#include <typeinfo>
#endif // BOOST_NO_RTTI
#endif // XPCOM_USE_STD_EXCEPTIONS
#include <xpcom/exception.hpp>
#include <xpcom/result_code.hpp>
#include <xpcom/impl/exception.hpp>

namespace xpcom {

namespace {

// Standard exception message
const char exception_msg[] = "xpcom.exception";

} // anonymous namespace

// exception
exception::exception() throw() :
    code_(NS_ERROR_FAILURE)
{
}

exception::exception(result code) throw() :
    code_(code)
{
}

const char* exception::what() const throw()
{
    return exception_msg;
}

// throw_exception_t
void detail::throw_exception_t::operator()() const
{
#ifdef XPCOM_USE_STD_EXCEPTIONS
    throw_runtime_error();
#else
    throw exception();
#endif
}

void detail::throw_exception_t::operator()(result r) const
{
#ifndef XPCOM_NO_BAD_ALLOC
    if (r == NS_ERROR_OUT_OF_MEMORY)
        throw_bad_alloc();
#endif // XPCOM_NO_BAD_ALLOC

#ifdef XPCOM_USE_STD_EXCEPTIONS
    switch (r.get()) {
        case NS_ERROR_FAILURE:
            throw_runtime_error();
            break;

        case NS_ERROR_INVALID_ARG:
            throw_invalid_argument();
            break;

#ifndef BOOST_NO_RTTI
        case NS_ERROR_NO_INTERFACE:
            throw_bad_cast();
            break;
#endif // BOOST_NO_RTTI

        default:
            throw exception(r);
            break;
    }

#else // XPCOM_USE_STD_EXCEPTIONS
    throw exception(r);
#endif // XPCOM_USE_STD_EXCEPTIONS
}

// For memory allocation error
#ifndef XPCOM_NO_BAD_ALLOC
void throw_bad_alloc()
{
    throw std::bad_alloc();
}
#else // XPCOM_NO_BAD_ALLOC
void throw_bad_alloc()
{
    throw exception(NS_ERROR_OUT_OF_MEMORY);
}
#endif // XPCOM_NO_BAD_ALLOC

// Standard error throws
#ifdef XPCOM_USE_STD_EXCEPTIONS
void throw_bad_cast()
{
#ifndef BOOST_NO_RTTI
    throw std::bad_cast();
#else
    throw exception(NS_ERROR_NO_INTERFACE);
#endif
}

void throw_invalid_argument(const char* msg)
{
    if (!msg)
        msg = exception_msg;
    throw std::invalid_argument(msg);
}

void throw_length_error(const char* msg)
{
    if (!msg)
        msg = exception_msg;
    throw std::length_error(msg);
}

void throw_out_of_range(const char* msg)
{
    if (!msg)
        msg = exception_msg;
    throw std::out_of_range(msg);
}

void throw_runtime_error(const char* msg)
{
    if (!msg)
        msg = exception_msg;
    throw std::runtime_error(msg);
}

void throw_range_error(const char* msg)
{
    if (!msg)
        msg = exception_msg;
    throw std::range_error(msg);
}

#else // XPCOM_USE_STD_EXCEPTIONS
void throw_bad_cast()
{
    throw exception(NS_ERROR_NO_INTERFACE);
}

void throw_invalid_argument(const char* /*msg*/)
{
    throw exception(NS_ERROR_INVALID_ARG);
}

void throw_length_error(const char* /*msg*/)
{
    throw exception(NS_ERROR_INVALID_ARG);
}

void throw_out_of_range(const char* /*msg*/)
{
    throw exception(NS_ERROR_INVALID_ARG);
}

void throw_runtime_error(const char* /*msg*/)
{
    throw exception(NS_ERROR_FAILURE);
}

void throw_range_error(const char* /*msg*/)
{
    throw exception(NS_ERROR_FAILURE);
}

#endif // XPCOM_USE_STD_EXCEPTIONS

void throw_not_implemented()
{
    throw exception(NS_ERROR_NOT_IMPLEMENTED);
}

namespace impl {

result_t translate_exception()
{
    result r;

    // XXX XPCOM_DISABLE_EXCEPTION_RETHROW_CATCH will not be checked

    try {
        throw;

    } catch (const exception& e) {
        r = e.code();

    } catch (const std::bad_alloc&) {
        r = NS_ERROR_OUT_OF_MEMORY;

#ifdef XPCOM_USE_STD_EXCEPTIONS
#ifndef BOOST_NO_RTTI
    } catch (const std::bad_cast&) {
        r = NS_ERROR_NO_INTERFACE;
#endif

    } catch (const std::invalid_argument&) {
        r = NS_ERROR_INVALID_ARG;

    } catch (const std::length_error&) {
        r = NS_ERROR_INVALID_ARG;

    } catch (const std::out_of_range&) {
        r = NS_ERROR_INVALID_ARG;
#endif // XPCOM_USE_STD_EXCEPTIONS

    } catch (const std::exception&) {
        r = NS_ERROR_FAILURE;

    } catch (...) {
        r = NS_ERROR_FAILURE;
    }

    return r;
}

} // namespace xpcom::impl

} // namespace xpcom
